/**
 *    Copyright 2009-2016 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.reflection;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Arrays;

/**
 * 主要是对Mybatis中需要的类或者接口解析反射时，</br>
 * 对其复杂的泛型进行解析保存为其自定义的Type体系接口（TypeParameterResolver中有实现类），</br>
 * 对其中的类型形参，使用类型实参表示。然后缓存起来，方便下次使用。
 * 
 * 工具类主要有三个对外开放的方法，分别是:</br>
 * {@link #resolveFieldType(Field, Type)} 解析字段的类型</br>
 * {@link #resolveReturnType(Method, Type)} 解析方法返回类型</br>
 * {@link #resolveParamTypes(Method, Type)} 解析方法入参的类型</br>
 * 上面三个方法获取到需要解析的类型后，调用{@link #resolveType(Type, Type, Class)}方法进行统一的处理。
 * resolveType方法根据入参的类型的类别，分别调用：</br>
 * {@link #resolveTypeVar(TypeVariable, Type, Class)} 类型变量解析</br>
 * {@link #resolveParameterizedType(ParameterizedType, Type, Class)} 参数化类型解析</br>
 * {@link #resolveGenericArrayType(GenericArrayType, Type, Class)} 泛型数组解析。
 * @author Iwao AVE!
 */
public class TypeParameterResolver {

  /**
   * 解析字段的类型
   * @param field
   * @param srcType 被反射时调用的类型，即被解析的字段是通过那个类型反射出来的
   * @return The field type as {@link Type}. If it has type parameters in the declaration,<br>
   *         they will be resolved to the actual runtime {@link Type}s.
   */
  public static Type resolveFieldType(Field field, Type srcType) {
	//获取需要解析的字段类型
    Type fieldType = field.getGenericType();
    //获取字段定义的类的class
    Class<?> declaringClass = field.getDeclaringClass();
    //解析
    return resolveType(fieldType, srcType, declaringClass);
  }

  /**
   * 解析方法返回类型
   * @return The return type of the method as {@link Type}. If it has type parameters in the declaration,<br>
   *         they will be resolved to the actual runtime {@link Type}s.
   */
  public static Type resolveReturnType(Method method, Type srcType) {
	//获取方法的返回类型
    Type returnType = method.getGenericReturnType();
    //获取方法定义的类的类型
    Class<?> declaringClass = method.getDeclaringClass();
    return resolveType(returnType, srcType, declaringClass);
  }

  /**
   * 解析方法入参的类型
   * @return The parameter types of the method as an array of {@link Type}s. If they have type parameters in the declaration,<br>
   *         they will be resolved to the actual runtime {@link Type}s.
   */
  public static Type[] resolveParamTypes(Method method, Type srcType) {
	//获取方法所有参数类型
    Type[] paramTypes = method.getGenericParameterTypes();
    //获取方法定义的类类型
    Class<?> declaringClass = method.getDeclaringClass();
    //定义存储结果的数组
    Type[] result = new Type[paramTypes.length];
    for (int i = 0; i < paramTypes.length; i++) {
      //对每个参数的类型进行解析
      result[i] = resolveType(paramTypes[i], srcType, declaringClass);
    }
    return result;
  }
  /**
   * 进行统一的处理类型的方法，其实又做了分发，到其他具体的方法来执行
   * @param type
   * @param srcType 被反射时调用的类型，即被解析的方法或字段是通过那个类型反射出来的
   * @param declaringClass 定义被解析的方法或字段的class类型，即这个方法/字段是定义在那个class中的
   * @return
   */
  private static Type resolveType(Type type, Type srcType, Class<?> declaringClass) {
    if (type instanceof TypeVariable) {//解析TypeVariable类型
      return resolveTypeVar((TypeVariable<?>) type, srcType, declaringClass);
    } else if (type instanceof ParameterizedType) { //解析ParameterizedType类型
      return resolveParameterizedType((ParameterizedType) type, srcType, declaringClass);
    } else if (type instanceof GenericArrayType) {//解析GenericArrayType类型
      return resolveGenericArrayType((GenericArrayType) type, srcType, declaringClass);
    } else {//如果为普通的Class类型就直接返回
      return type;
    }
  }
  /**
   * 泛型数组解析
   * @param genericArrayType
   * @param srcType
   * @param declaringClass
   * @return
   */
  private static Type resolveGenericArrayType(GenericArrayType genericArrayType, Type srcType, Class<?> declaringClass) {
	//去掉一层[]后的泛型类型变量
	Type componentType = genericArrayType.getGenericComponentType();
    Type resolvedComponentType = null;
    //根据去掉一维数组后的类型变量，在根据其类型递归解析
    if (componentType instanceof TypeVariable) {//如果去掉后为TypeVariable类型，则调用resolveTypeVar方法
      resolvedComponentType = resolveTypeVar((TypeVariable<?>) componentType, srcType, declaringClass);
    } else if (componentType instanceof GenericArrayType) {//如果去掉仍为GenericArrayType类型，则递归调用resolveGenericArrayType方法
      resolvedComponentType = resolveGenericArrayType((GenericArrayType) componentType, srcType, declaringClass);
    } else if (componentType instanceof ParameterizedType) {//如果去掉后为ParameterizedType类型，则调用resolveParameterizedType方法处理
      resolvedComponentType = resolveParameterizedType((ParameterizedType) componentType, srcType, declaringClass);
    }
    if (resolvedComponentType instanceof Class) {//如果处理后的结果为基本的Class类型，则返回对应的Class的数组类型（处理N[][]）。
      return Array.newInstance((Class<?>) resolvedComponentType, 0).getClass();
    } else {//否则包装为自定义的GenericArrayTypeImpl类型
      return new GenericArrayTypeImpl(resolvedComponentType);
    }
  }
  /**
   * 参数化类型解析
   * @param parameterizedType
   * @param srcType
   * @param declaringClass
   * @return
   */
  private static ParameterizedType resolveParameterizedType(ParameterizedType parameterizedType, Type srcType, Class<?> declaringClass) {
	//获取泛型的基本类型
	Class<?> rawType = (Class<?>) parameterizedType.getRawType();
	//获取泛型中的类型实参
	Type[] typeArgs = parameterizedType.getActualTypeArguments();
	//递归处理其类型实参，存储结果的对象
    Type[] args = new Type[typeArgs.length];
    //遍历typeArgs，分别处理并把结果存储到args中
    for (int i = 0; i < typeArgs.length; i++) {
      if (typeArgs[i] instanceof TypeVariable) {//解析TypeVariable类型
        args[i] = resolveTypeVar((TypeVariable<?>) typeArgs[i], srcType, declaringClass);
      } else if (typeArgs[i] instanceof ParameterizedType) {//解析ParameterizedType类型
        args[i] = resolveParameterizedType((ParameterizedType) typeArgs[i], srcType, declaringClass);
      } else if (typeArgs[i] instanceof WildcardType) {//是解析WildcardType类型（其类型实参是通配符表达式）
        args[i] = resolveWildcardType((WildcardType) typeArgs[i], srcType, declaringClass);
      } else {//普通Class类型，直接返回
        args[i] = typeArgs[i];
      }
    }
    return new ParameterizedTypeImpl(rawType, null, args);
  }
  /**
   * 在对通配符进行解析时，主要对其上下限的类型进行解析
   * @param wildcardType
   * @param srcType
   * @param declaringClass
   * @return
   */
  private static Type resolveWildcardType(WildcardType wildcardType, Type srcType, Class<?> declaringClass) {
	//获取下限
	Type[] lowerBounds = resolveWildcardTypeBounds(wildcardType.getLowerBounds(), srcType, declaringClass);
	//获取上限
	Type[] upperBounds = resolveWildcardTypeBounds(wildcardType.getUpperBounds(), srcType, declaringClass);
	//包装成自定义的WildcardTypeImpl类型返回
	return new WildcardTypeImpl(lowerBounds, upperBounds);
  }
  /**
   * 对其上下限进行解析时，主要根据其不同的类型调用上面的以有解析方法进行递归解析
   * @param bounds
   * @param srcType
   * @param declaringClass
   * @return
   */
  private static Type[] resolveWildcardTypeBounds(Type[] bounds, Type srcType, Class<?> declaringClass) {
    Type[] result = new Type[bounds.length];
    for (int i = 0; i < bounds.length; i++) {
      //根据上下限不同的类型，进行解析
      if (bounds[i] instanceof TypeVariable) {
        result[i] = resolveTypeVar((TypeVariable<?>) bounds[i], srcType, declaringClass);
      } else if (bounds[i] instanceof ParameterizedType) {
        result[i] = resolveParameterizedType((ParameterizedType) bounds[i], srcType, declaringClass);
      } else if (bounds[i] instanceof WildcardType) {
        result[i] = resolveWildcardType((WildcardType) bounds[i], srcType, declaringClass);
      } else {
        result[i] = bounds[i];
      }
    }
    return result;
  }
  /**
   * 类型变量解析,解析具体的类型变量对应的类型。
   * 1.如果srcType的Class类型和declaringClass为同一个类，表示获取该类型变量时被反射的类型就是其定义的类型，则取该类型变量定义是有没有上限，如果有则使用其上限代表其类型，否则就用Object。
   * 2.如果不是，则代表declaringClass是srcType的父类或者实现的接口，则解析继承中有没有定义其代表的类型
   * @param typeVar
   * @param srcType
   * @param declaringClass
   * @return
   */
  private static Type resolveTypeVar(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass) {
    Type result = null;
    Class<?> clazz = null;
    //判断srcType是否为Class/ParameterizedType类型，如果不是这两种类型这抛出异常
    if (srcType instanceof Class) {
      clazz = (Class<?>) srcType;
    } else if (srcType instanceof ParameterizedType) {
      ParameterizedType parameterizedType = (ParameterizedType) srcType;
      clazz = (Class<?>) parameterizedType.getRawType();
    } else {
      throw new IllegalArgumentException("The 2nd arg must be Class or ParameterizedType, but was: " + srcType.getClass());
    }
    //如果declaringClass和srcType的实际类型一致则表示无法获取其类型实参。如果typeVar有上限限定则返回其上限，否则返回Object处理
    if (clazz == declaringClass) {
      Type[] bounds = typeVar.getBounds();
      if(bounds.length > 0) {
        return bounds[0];
      }
      return Object.class;
    }

    Type superclass = clazz.getGenericSuperclass();
    result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superclass);
    if (result != null) {
      return result;
    }
    /**
     * 如果父类的定义中没有，则处理其实现的接口。
     */
    Type[] superInterfaces = clazz.getGenericInterfaces();
    for (Type superInterface : superInterfaces) {
      result = scanSuperTypes(typeVar, srcType, declaringClass, clazz, superInterface);
      if (result != null) {
        return result;
      }
    }
    //如果父类或者实现的接口中都没有获取到形参对应的实参，则返回Object.class
    return Object.class;
  }
  /**
   *  通过对父类/接口的扫描获取其typeVar指代的实际类型
   */
  private static Type scanSuperTypes(TypeVariable<?> typeVar, Type srcType, Class<?> declaringClass, Class<?> clazz, Type superclass) {
    Type result = null;
    /*
     * 判断处理的父类superclass是否为参数化类型，如果不是则代表declaringClass和superclass的基本Class
     * 类型不是同一个类。
     */
    if (superclass instanceof ParameterizedType) {
      //如果为ParameterizedType，则获取它基本类型
      ParameterizedType parentAsType = (ParameterizedType) superclass;
      Class<?> parentAsClass = (Class<?>) parentAsType.getRawType();
      if (declaringClass == parentAsClass) {
    	//如果declaringClass和parentAsClass表示同一类型，则通过typeVar在declaringClass的泛型形参的index获取其在supperClass中定义的类型实参
        Type[] typeArgs = parentAsType.getActualTypeArguments();
        TypeVariable<?>[] declaredTypeVars = declaringClass.getTypeParameters();
        for (int i = 0; i < declaredTypeVars.length; i++) {
        	//循环判断当前处理的类型是否属于所属的类型描述符中的变量
          if (declaredTypeVars[i] == typeVar) {
        	  /*
               *如果supperClass中定义的类型形参还是类型变量则取srcType中的类型形参的定义
               * 如果srcType中的类型形参还是类型变量则不处理。
               */
            if (typeArgs[i] instanceof TypeVariable) {
            	//其子类中的所有泛型描述符
              TypeVariable<?>[] typeParams = clazz.getTypeParameters();
              for (int j = 0; j < typeParams.length; j++) {
                if (typeParams[j] == typeArgs[i]) {
                	 //判断是否为ParameterizedType，则去实际代表的类型
                  if (srcType instanceof ParameterizedType) {
                    result = ((ParameterizedType) srcType).getActualTypeArguments()[j];
                  }
                  break;
                }
              }
            } else {
            	//如果不是TypeVariable，直接取对应的类型
              result = typeArgs[i];
            }
          }
        }
      } else if (declaringClass.isAssignableFrom(parentAsClass)) {
    	//通过判断superclass是否是declaringClass的子类（由于java类可以实现多个接口），进行递归解析
        result = resolveTypeVar(typeVar, parentAsType, declaringClass);
      }
    } else if (superclass instanceof Class) {
    	//如果superclass为Class类型，通过判断superclass是否是declaringClass的子类（由于java类可以实现多个接口），进行递归解析
      if (declaringClass.isAssignableFrom((Class<?>) superclass)) {
        result = resolveTypeVar(typeVar, superclass, declaringClass);
      }
    }
    return result;
  }

  private TypeParameterResolver() {
    super();
  }

  static class ParameterizedTypeImpl implements ParameterizedType {
    private Class<?> rawType;

    private Type ownerType;

    private Type[] actualTypeArguments;

    public ParameterizedTypeImpl(Class<?> rawType, Type ownerType, Type[] actualTypeArguments) {
      super();
      this.rawType = rawType;
      this.ownerType = ownerType;
      this.actualTypeArguments = actualTypeArguments;
    }

    @Override
    public Type[] getActualTypeArguments() {
      return actualTypeArguments;
    }

    @Override
    public Type getOwnerType() {
      return ownerType;
    }

    @Override
    public Type getRawType() {
      return rawType;
    }

    @Override
    public String toString() {
      return "ParameterizedTypeImpl [rawType=" + rawType + ", ownerType=" + ownerType + ", actualTypeArguments=" + Arrays.toString(actualTypeArguments) + "]";
    }
  }

  static class WildcardTypeImpl implements WildcardType {
    private Type[] lowerBounds;

    private Type[] upperBounds;

    private WildcardTypeImpl(Type[] lowerBounds, Type[] upperBounds) {
      super();
      this.lowerBounds = lowerBounds;
      this.upperBounds = upperBounds;
    }

    @Override
    public Type[] getLowerBounds() {
      return lowerBounds;
    }

    @Override
    public Type[] getUpperBounds() {
      return upperBounds;
    }
  }

  static class GenericArrayTypeImpl implements GenericArrayType {
    private Type genericComponentType;

    private GenericArrayTypeImpl(Type genericComponentType) {
      super();
      this.genericComponentType = genericComponentType;
    }

    @Override
    public Type getGenericComponentType() {
      return genericComponentType;
    }
  }
}
